/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.config.spring.context.annotation;

import com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBinding;
import com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBindings;
import org.apache.dubbo.config.*;
import org.apache.dubbo.config.spring.ConfigCenterBean;
import org.springframework.context.annotation.Configuration;

/**
 * Dubbo {@link AbstractConfig Config} {@link Configuration}
 *
 * @revised 2.7.5
 * @see Configuration
 * @see EnableConfigurationBeanBindings
 * @see EnableConfigurationBeanBinding
 * @see ApplicationConfig
 * @see ModuleConfig
 * @see RegistryConfig
 * @see ProtocolConfig
 * @see MonitorConfig
 * @see ProviderConfig
 * @see ConsumerConfig
 * @see org.apache.dubbo.config.ConfigCenterConfig
 * @since 2.5.8
 */


//Dubbo RPC组件
//下面按照上面对RPC的拆解来分析下Dubbo对应的模块，这里只是为了展现一个大体的框架，不会对每个模块做详细的解析，所以有个大概印象就可以了。后续分析到具体模块的源码，可以回来翻查一下。
//为了和思维方式对上，我们从左至右过一下RPC框架图，先从客户端调用接口开始。
//
//代理
//Proxy
//第一步是接口要有个代理实现，下面是Dubbo官方的Consumer例子，因为绝大多数人都是Dubbo和Spring一起用的，所以选用SpringBoot的例子：
//
//@EnableAutoConfiguration
//public class DubboAutoConfigurationConsumerBootstrap {
//
//    private final Logger logger = LoggerFactory.getLogger(getClass());
//
//    @Reference(version = "1.0.0", url = "dubbo://127.0.0.1:12345")
//    private DemoService demoService;
//
//    public static void main(String[] args) {
//        SpringApplication.run(DubboAutoConfigurationConsumerBootstrap.class).close();
//    }
//
//    @Bean
//    public ApplicationRunner runner() {
//        return args -> logger.info(demoService.sayHello("mercyblitz"));
//    }
//}
//代码中DemoService就是远程API接口，Dubbo使用自定义的@Reference注解来注入，被注入的自然就是Dubbo的Proxy实现了，对这个接口的调用，会通过Proxy将请求发到127.0.0.1的12345端口。
//Dubbo通过调用代理工厂ProxyFactory的getProxy来获得一个接口的代理实现类。
//
//@SPI("javassist")
//public interface ProxyFactory {
//
//    /**
//     * create proxy.
//     *
//     * @param invoker
//     * @return proxy
//     */
//    @Adaptive({PROXY_KEY})
//    <T> T getProxy(Invoker<T> invoker) throws RpcException;
//}
//可以看到，这个代理工厂也是一个接口，Dubbo中提供了两种实现，一种使用的JDK中的动态代理实现（JdkProxyFactory），这个就是用的java.lang.reflect.Proxy.newProxyInstance()方法，关于这个的具体原理和用法网上很多，这里就不解释了。
//Dubbo默认采用的是另一个实现JavassistProxyFactory，Javaassist是一个运行期字节码工具，它允许在运行时动态编译代码成class。Dubbo默认做法就是在初始化时生成一个接口实现类的源代码，通过javaassist编译后加载，Consumer实际上就是调用的这个类。
//上面的源代码在dubbo-rpc的dubbo-rpc-api模块中。
//
//代理实现原理
//代理有了，做的事情无非就是按传输层要求的格式把调用参数封装成一个Request，通过传输层发出去，比如通过http调用的话就封装成一个HttpRequest。但是在Dubbo这里没有这么简单，因为Dubbo是需要支持多种协议的，每个接口用的网络接口调用Client可能都不一样，而且还要保证扩展性，方便以后添加新的协议实现。
//有3个类需要重点关注下：Protocol，Invoker和Invocation。
//
//Invoker
//Invoker是调用动作封装类，作用类似于java.lang.Runnable相对于Thread。 调用它的invoke()方法发起一次调用，这次调用有可能是远程有可能是本地，对调用方来说是透明的。一般来说，每个远程服务接口都会对应有一个Invoker实例。
//public interface Invoker<T> extends Node {
//    /**
//     * 获取Invoker调用的target接口类
//     */
//    Class<T> getInterface();
//    /**
//     * 发起一次调用
//     */
//    Result invoke(Invocation invocation) throws RpcException;
//
//}
//Invocation
//invoke() 操作的输入参数，就是把真实调用的method，输入参数等封装一下。下面是它定义的部分方法：
//public interface Invocation {
//    ...
//    /**
//     * 获取方法名
//     */
//    String getMethodName();
//    /**
//     * 获取接口名
//     */
//    String getServiceName();
//    /**
//     * 参数类型
//     */
//    Class<?>[] getParameterTypes();
//   /**
//     * 调用参数
//     */
//    Object[] getArguments();
//     ...
//}
//Protocol
//Dubbo对接口协议的抽象，主要是两个功能，对于Provider来说通过Protocol把本地接口暴露成一个指定协议Server；对于Consumer来说可以获取一个针对特定接口协议的Invoker
//@SPI("dubbo")
//public interface Protocol {
//    //暴露服务接口
//    @Adaptive
//    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
//
//     //获取接口调用的Invoker
//    @Adaptive
//    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
//}
//看完上面几个接口，把关系捋一下，Consumer获取到接口的Proxy后，调用方法时，Proxy根据远程服务的配置协议找对应的Protocol实现，默认是DubboProtocol。然后，调用Protocol的refer方法获取到Invoker实现，比如从DubboProtocol获取到的就是DubboInvoker。然后，将Consumer原始的输入参数封装成一个Invocation（对应于远程调用就是RpcInvocation），调用Invoker的invoke() 方法。
//
//注意以上只是逻辑流程，不是真正的代码调用流程，因为代码实现中对象都是提前初始化的，初始化的代码流程和逻辑流程不是完全一样。
public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
    })
    public static class Single {

    }

    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
    })
    public static class Multiple {

    }
}
